package hk.linkcredit.walletservice.wallet.service.impl;

import com.alibaba.fastjson2.JSONObject;
import hk.linkcredit.walletservice.constant.Status;
import hk.linkcredit.walletservice.constant.WalletConstants;
import hk.linkcredit.walletservice.exception.BizException;
import hk.linkcredit.walletservice.utils.StringUtils;
import hk.linkcredit.walletservice.utils.core.RedisCache;
import hk.linkcredit.walletservice.wallet.domain.entity.WalletOrder;
import hk.linkcredit.walletservice.wallet.domain.vo.WalletAccountInfo;
import hk.linkcredit.walletservice.wallet.domain.vo.WalletOrderReq;
import hk.linkcredit.walletservice.wallet.domain.vo.WalletRepayReq;
import hk.linkcredit.walletservice.wallet.domain.vo.WalletWithdrawReq;
import hk.linkcredit.walletservice.wallet.service.*;
import hk.linkcredit.walletservice.wallet.service.strategy.OrderStatusStrategy;
import hk.linkcredit.walletservice.wallet.service.strategy.OrderStatusStrategyFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

/**
 * @Author: jamnsin
 * @Date: 2022/6/12 22:49
 */
@Slf4j
@Service
public class WalletServiceImpl implements WalletService {

    @Autowired
    WalletAccountService walletAccountService;

    @Autowired
    WalletOrderService walletOrderService;

    @Autowired
    WalletAmountService walletAmountService;

    @Autowired
    WalletFlowService walletFlowService;

    @Autowired
    OrderStatusStrategyFactory orderStatusStrategyFactory;

    @Autowired
    SysDictDataService dictDataService;

    @Autowired
    RedisCache redisCache;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public JSONObject walletWithdraw(String orderType, WalletWithdrawReq walletWithdrawReq) {
        log.info("进入钱包提现流程 >>> orderType = {}，walletWithdrawReq = {}", orderType, walletWithdrawReq);

        WalletAccountInfo walletAccount = walletAccountService.getWalletAccountInfo(walletWithdrawReq.getCustNo());

        // 校验钱包额度是否足够
        walletAccountService.checkWalletBalance(walletWithdrawReq.getWalletType(), walletWithdrawReq.getApplyAmount(), walletAccount);

        // 生成提现订单
        BigDecimal actualAmount = getActualAmount(walletWithdrawReq, walletAccount);
        WalletOrder walletOrder = walletOrderService.generateWalletOrder(orderType, actualAmount, walletWithdrawReq, walletAccount);

        JSONObject withdrawJson = new JSONObject();
        withdrawJson.put("orderSerialNo", walletOrder.getSerialNo());
        withdrawJson.put("actualAmount", actualAmount);

        // 冻结钱包对应金额
        BigDecimal freezeAmt = actualAmount.add(walletOrder.getServiceFee());
        walletAmountService.freezeWalletAmt(walletWithdrawReq.getWalletType(), freezeAmt, walletAccount);

        log.info("结束钱包提现流程 >>> ");
        return withdrawJson;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public JSONObject walletRepay(String orderType, WalletRepayReq walletRepayReq) {
        log.info("进入钱包还款流程 >>> orderType = {}，walletRepayReq = {}", orderType, walletRepayReq);

        WalletAccountInfo walletAccount = walletAccountService.getWalletAccountInfo(walletRepayReq.getCustNo());

        BigDecimal submitAmt = walletRepayReq.getApplyAmount();
        BigDecimal availableBalance = getAvailableAmt(WalletConstants.WALLET_TYPE_BALANCE_1, walletAccount.getWalletStatus(), walletAccount.getBalance(), walletAccount.getFrozenBalance());
        BigDecimal availableBonus = getAvailableAmt(WalletConstants.WALLET_TYPE_BONUS_2, walletAccount.getWalletStatus(), walletAccount.getBonus(), walletAccount.getFrozenBonus());

        BigDecimal actualAmt = getActualAmt(submitAmt, availableBalance, availableBonus);
        if (actualAmt.compareTo(BigDecimal.ZERO) <= 0) {
            log.debug("钱包还款实际可用余额为 0，直接返回");
            JSONObject rst = new JSONObject();
            rst.put("actualAmt", BigDecimal.ZERO);
            return rst;
        }

        JSONObject repayAmtSplit = walletAccountService.splitAmount(walletAccount.getWalletStatus(), actualAmt, availableBalance, availableBonus);
        String orderSerialNo = walletOrderService.generateWalletOrder(orderType, repayAmtSplit, walletRepayReq, walletAccount);
        log.debug("还款订单流水号：{}", orderSerialNo);

        // 冻结钱包对应金额
        walletAmountService.freezeWalletAmt(repayAmtSplit, walletAccount);

        JSONObject repayRst = new JSONObject();
        repayRst.put("actualAmt", actualAmt);
        repayRst.put("orderSerialNo", orderSerialNo);

        log.info("结束钱包还款流程 >>> ");
        return repayRst;
    }

    @Override
    public void updateWalletOrder(WalletOrderReq walletOrderReq) {
        OrderStatusStrategy orderStatusStrategy = orderStatusStrategyFactory.getOrderStatusStrategy(getWalletOrderStatus(walletOrderReq.getApprovalStatus()));
        orderStatusStrategy.dealWalletOrder(walletOrderReq);
    }

    private BigDecimal getActualAmt(BigDecimal submitAmt, BigDecimal availableBalance, BigDecimal availableBonus) {
        log.info("获取钱包还款实际可用金额 >>> submitAmt = {}，availableBalance = {}，availableBonus = {}", submitAmt, availableBalance, availableBonus);

        BigDecimal actualAmt = BigDecimal.ZERO;

        BigDecimal availableAmt = availableBalance.add(availableBonus);
        if (availableAmt.compareTo(BigDecimal.ZERO) <= 0 || submitAmt.compareTo(BigDecimal.ZERO) <= 0) {
            actualAmt = BigDecimal.ZERO;
            log.info("钱包还款可用金额为 0");
        }

        if (availableAmt.compareTo(submitAmt) >= 0) {
            actualAmt = submitAmt;
            log.info("钱包还款满足申请金额：{}，当前余额：{}", actualAmt, availableAmt);
        }

        if (availableAmt.compareTo(submitAmt) < 0) {
            actualAmt = availableAmt;
            log.info("钱包还款实际可用金额：{}, 当前余额：{}", actualAmt, availableAmt);
        }

        return actualAmt;
    }

    private BigDecimal getActualAmount(WalletWithdrawReq walletWithdrawReq, WalletAccountInfo walletAccount) {
        log.info("获取钱包提现实际金额 >>> walletWithdrawReq = {}，walletAccount = {}", walletWithdrawReq, walletAccount);

        BigDecimal availableAmt = BigDecimal.ZERO;
        BigDecimal actualAmt = BigDecimal.ZERO;
        BigDecimal serviceFee = BigDecimal.ZERO;
        BigDecimal applyAmt = walletWithdrawReq.getApplyAmount();

        if (WalletConstants.WALLET_TYPE_BALANCE_1.equals(walletWithdrawReq.getWalletType())) {
            availableAmt = getAmt(walletAccount.getBalance()).subtract(getAmt(walletAccount.getFrozenBalance()));
            serviceFee = getAmt(applyAmt).multiply(walletAccount.getServiceRate());
        } else if (WalletConstants.WALLET_TYPE_BONUS_2.equals(walletWithdrawReq.getWalletType())) {
            availableAmt = getAmt(walletAccount.getBonus()).subtract(getAmt(walletAccount.getFrozenBonus()));
            serviceFee = getAmt(applyAmt).multiply(walletAccount.getServiceRate());
        }

        if (applyAmt.compareTo(availableAmt) == 0) {
            actualAmt = availableAmt.subtract(serviceFee);
        }

        if (applyAmt.compareTo(availableAmt.subtract(serviceFee)) <= 0) {
            actualAmt = applyAmt;
        } else {
            actualAmt = availableAmt.subtract(serviceFee);
        }

        log.info("钱包提现实际金额为：{}", actualAmt);
        return actualAmt;
    }

    private BigDecimal getAvailableAmt(String walletType, String walletStatus, BigDecimal amount, BigDecimal frozenAmt) {
        log.info("获取钱包可用余额 >>> walletType = {}, walletStatus = {}，amount = {}，frozenAmt = {}", walletType, walletStatus, amount, frozenAmt);

        if (StringUtils.equals(walletStatus, WalletConstants.WALLET_STATUS_LOCK)) {
            log.debug("钱包锁，可用金额为 0");
            return BigDecimal.ZERO;
        }

        if (StringUtils.equals(walletType, WalletConstants.WALLET_TYPE_BALANCE_1) && StringUtils.equals(walletStatus, WalletConstants.WALLET_STATUS_BALANCE_LOCK)) {
            log.debug("余额锁，可用金额为 0");
            return BigDecimal.ZERO;
        }

        if (StringUtils.equals(walletType, WalletConstants.WALLET_TYPE_BONUS_2) && StringUtils.equals(walletStatus, WalletConstants.WALLET_STATUS_BONLUS_LOCK)) {
            log.debug("奖励锁，可用金额为 0");
            return BigDecimal.ZERO;
        }

        return getAmt(getAmt(amount).subtract(getAmt(frozenAmt)));
    }

    private BigDecimal getAmt(BigDecimal amount) {
        return amount.compareTo(BigDecimal.ZERO) <= 0 ? BigDecimal.ZERO : amount;
    }

    private String getWalletOrderStatus(String orderStatus) {
        String status = null;

        switch (orderStatus) {
            case WalletConstants.WALLET_ORDER_STATUS_REJECTED:
                status = WalletConstants.WALLET_ORDER_STATUS_0;
                break;
            case WalletConstants.WALLET_ORDER_STATUS_APPROVED:
                status = WalletConstants.WALLET_ORDER_STATUS_1;
                break;
            case WalletConstants.WALLET_ORDER_STATUS_UNDER_REVIEW:
                status = WalletConstants.WALLET_ORDER_STATUS_9;
                break;
            case WalletConstants.WALLET_ORDER_STATUS_SUCCESS:
                status = WalletConstants.WALLET_ORDER_STATUS_3;
                break;
            case WalletConstants.WALLET_ORDER_STATUS_FAILUER:
                status = WalletConstants.WALLET_ORDER_STATUS_4;
                break;
            case WalletConstants.WALLET_ORDER_STATUS_PENDING:
                status = WalletConstants.WALLET_ORDER_STATUS_2;
                break;
            case WalletConstants.WALLET_ORDER_STATUS_RETRIED:
                status = WalletConstants.WALLET_ORDER_STATUS_5;
                break;
            default:
                throw new BizException(Status.WALLE_ORDER_STATUS_CANNOT_SUPPORT);
        }

        return status;
    }

}
